<!doctype html>
<html lang="zh-CN">
<head><meta name="generator" content="Hexo 3.8.0">
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=11,IE=10,IE=9,IE=8">
    <meta name="baidu-site-verification" content="dIcXMeY8Ya">
    
    <title>Linux CFS调度器之唤醒WAKE_AFFINE 机制--Linux进程的管理与调度(三十一) | OSKernelLAB</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
    <meta name="keywords" content="gatieme, 成坚, 内核, linux, kernel">
    <meta name="description" content="内核杂谈">

    
    <link rel="alternative" href="/atom.xml" title="OSKernelLAB" type="application/atom+xml">
    
    
    <link rel="icon" href="/favicon.ico">
    
    <link rel="stylesheet" href="/css/style.css?v=1615735433153">
    <!--[if lt IE 9]>
    <script src="/js/html5.js"></script>
    <![endif]-->
    
<script>
    var _hmt = _hmt || [];
    (function() {
        var hm = document.createElement("script");
        hm.src="https://hm.baidu.com/hm.js?1a8f9a67050c5fdb4c73cbbc00124bd8"
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
    })();
</script>


</head>
</html>
<body class="home">
    <!--[if lt IE 9]>
    <div class="browsehappy">
        当前网页 <strong>不支持</strong>
        你正在使用的浏览器. 为了正常的访问, 请 <a href="http://browsehappy.com/">升级你的浏览器</a>.
    </div>
    <![endif]-->

    <!-- 博客头部 -->
    <header class="header">
    <section class="container header-main">
        <div class="logo">
            <a href="/">
                <div class="cover">
                    <span class="name">OSKernelLAB</span>
                    <span class="description">OS内核实验室</span>
                </div>
            </a>
        </div>
        <div class="dropnav iconfont icon-nav" id="JELON__btnDropNav"></div>
        <ul class="menu hidden" id="JELON__menu">
            
            <li rel="/2020/05/24/2020/0524-0001-linux_cfs_scheduler_31_wake_affine/index.html" class="item ">
                <a href="/" title="首页" class="iconfont icon-home">&nbsp;首页</a>
            </li>
            
            <li rel="/2020/05/24/2020/0524-0001-linux_cfs_scheduler_31_wake_affine/index.html" class="item ">
                <a href="/archives/" title="归档" class="iconfont icon-archives">&nbsp;归档</a>
            </li>
            
            <li rel="/2020/05/24/2020/0524-0001-linux_cfs_scheduler_31_wake_affine/index.html" class="item ">
                <a href="/lab/" title="实验室" class="iconfont icon-archives">&nbsp;实验室</a>
            </li>
            
            <li rel="/2020/05/24/2020/0524-0001-linux_cfs_scheduler_31_wake_affine/index.html" class="item ">
                <a href="/navigation/" title="网址导航" class="iconfont icon-archives">&nbsp;网址导航</a>
            </li>
            
            <li rel="/2020/05/24/2020/0524-0001-linux_cfs_scheduler_31_wake_affine/index.html" class="item ">
                <a href="/about/" title="关于" class="iconfont icon-staff">&nbsp;关于</a>
            </li>
            
            <li rel="/2020/05/24/2020/0524-0001-linux_cfs_scheduler_31_wake_affine/index.html" class="item ">
                <a href="/comment/" title="留言" class="iconfont icon-comment">&nbsp;留言</a>
            </li>
            
        </ul>
        <div class="profile clearfix">
            <div class="feeds fl">
                
                
                <p class="links">
                    
                        <a href="https://github.com/gatieme" target="_blank">Github</a>
                        |
                    
                        <a href="https://github.com/gatieme/gatieme.github.io" target="_blank">Hosted by Github Pages</a>
                        
                    
                </p>
                <p class="sns">
                    
                        <a href="https://blog.csdn.net/gatieme" class="csdn" target="_blank"><b>■</b> CSDN 博客</a>
                    
                        <a href="https://www.facebook.com/gatieme" class="qqweibo" target="_blank"><b>■</b> Facebook</a>
                    
                    <a href="javascript: void(0);" class="wechat">
                        <b>■</b>
                        公众号
                        <span class="popover">
                            <img src="/img/wechat_mp.jpg" width="120" height="120" alt="我的微信订阅号">
                            <i class="arrow"></i>
                        </span>
                    </a>
                </p>
                
            </div>
            <div class="avatar fr">
                <img src="/img/gatieme.jpg" alt="avatar" title="Gatieme-(成坚)">
            </div>
        </div>
    </section>
</header>


    <!-- 博客正文 -->
    <div class="container body clearfix">
        <section class="content">
            <div class="content-main widget">
                <!-- 文章页 -->
<!-- 文章 -->
<article class="post article">
    <header class="text-center">
        <h3 class="post-title"><span>Linux CFS调度器之唤醒WAKE_AFFINE 机制--Linux进程的管理与调度(三十一)</span></h3>
    </header>
    <p class="post-meta text-center">
        gatieme 发表于
        <time datetime="2020-05-24T07:58:00.000Z">2020-05-24</time>
    </p>
    <div id="JELON__articlePostContent" class="post-content">
        <table>
<thead>
<tr>
<th>日期</th>
<th style="text-align:center">内核版本</th>
<th style="text-align:center">架构</th>
<th style="text-align:center">作者</th>
<th style="text-align:center">GitHub</th>
<th style="text-align:center">CSDN</th>
</tr>
</thead>
<tbody>
<tr>
<td>2016-0729</td>
<td style="text-align:center"><a href="http://lxr.free-electrons.com/source/?v=4.6" target="_blank" rel="noopener">Linux-4.6</a></td>
<td style="text-align:center">X86 &amp; arm</td>
<td style="text-align:center"><a href="https://blog.csdn.net/gatieme/article/details/106315848" target="_blank" rel="noopener">gatieme</a></td>
<td style="text-align:center"><a href="https://github.com/gatieme/LDD-LinuxDeviceDrivers/tree/master/study/kernel/01-process/05-schedule/07-cfs/08-wake_affine" target="_blank" rel="noopener">LinuxDeviceDrivers</a></td>
<td style="text-align:center"><a href="http://blog.csdn.net/gatieme/article/details/51456569" target="_blank" rel="noopener">Linux进程管理与调度</a></td>
</tr>
</tbody>
</table>
<blockquote>
<p>本文更新记录<br>20200513 更新了前言背景内容, 引入 WAKE_AFFINE 机制. 让大家对 WAKE_AFFINE 的目的有一个清楚认识.</p>
</blockquote>
<h1 id="1-wake-affine-机制"><a href="#1-wake-affine-机制" class="headerlink" title="1 wake_affine 机制"></a>1 wake_affine 机制</h1><hr>
<h2 id="1-1-引入-WAKE-AFFINE-的背景"><a href="#1-1-引入-WAKE-AFFINE-的背景" class="headerlink" title="1.1    引入 WAKE_AFFINE 的背景"></a>1.1    引入 WAKE_AFFINE 的背景</h2><hr>
<p>当进程被唤醒的时候（try_to_wake_up）, 需要用 select_task_rq_fair为该 task 选择一个合适的CPU(runqueue), 接着会通过 check_preempt_wakeup 去看被唤醒的进程是否要抢占所在 CPU 的当前进程.</p>
<blockquote>
<p>关于唤醒抢占的内容, 请参考 <a href="https://blog.csdn.net/gatieme/article/details/51872831" target="_blank" rel="noopener">Linux唤醒抢占—-Linux进程的管理与调度(二十三）</a></p>
<p>调度器对之前 SLEEP 的进程唤醒后重新入 RUNQ 的时候, 会对进程做一些补偿, 请参考 <a href="https://blog.csdn.net/gatieme/article/details/52068061" target="_blank" rel="noopener">Linux CFS调度器之唤醒补偿–Linux进程的管理与调度(三十）</a></p>
</blockquote>
<p>这个选核的过程我们一般称之为 BALANCE_WAKE. 为了能清楚的描述这个场景, 我们定义</p>
<ul>
<li>执行唤醒的那个进程是 waker</li>
<li>而被唤醒的进程是 wakee</li>
</ul>
<p>Wakeup有两种, 一种是sync wakeup, 另外一种是non-sync wakeup。</p>
<ul>
<li>所谓 sync wakeup 就是 waker 在唤醒 wakee 的时候就已经知道自己很快就进入 sleep 状态, 而在调用 try_to_wake_up 的时候最好不要进行抢占, 因为 waker 很快就主动发起调度了。此外, 一般而言, waker和wakee会有一定的亲和性（例如它们通过share memory进行通信）, 在SMP场景下, waker和wakee调度在一个CPU上执行的时候往往可以获取较佳的性能。而如果在try_to_wake_up的时候就进行调度, 这时候wakee往往会调度到系统中其他空闲的CPU上去。这时候, 通过sync wakeup, 我们往往可以避免不必要的CPU bouncing。</li>
<li>对于non-sync wakeup而言, waker和wakee没有上面描述的同步关系, waker在唤醒wakee之后, 它们之间是独立运作, 因此在唤醒的时候就可以尝试去触发一次调度。</li>
</ul>
<p>当然, 也不是说sync wakeup就一定不调度, 假设waker在CPU A上唤醒wakee, 而根据wakee进程的cpus_allowed成员发现它根本不能在CPU A上调度执行, 那么管他sync不sync, 这时候都需要去尝试调度(调用reschedule_idle函数), 反正waker和wakee命中注定是天各一方(在不同的CPU上执行).</p>
<p>select_task_rq_fair 的原型如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">select_task_rq_fair</span><span class="params">(struct task_struct *p, <span class="keyword">int</span> prev_cpu, <span class="keyword">int</span> sd_flag, <span class="keyword">int</span> wake_flags)</span></span></span><br></pre></td></tr></table></figure>
<p>在 try_to_wake_up 场景其中 p 是待唤醒进程, prev_cpu 是进程上次运行的 CPU, 一般 sd_flag 是 BALANCE_WAKE, 因此其实wakeup 的过程也可以理解为一次主动 BALANCE 的过程, 成为 WAKEUP BALANCE, 只不过只是为一个进程选择唤醒到的 CPU. wake_flags 用于表示是 sync wakeup 还是 non-sync wakeup.</p>
<p>我们首先看看UP上的情况。这时候waker和wakee在同一个CPU上运行（当然系统中也只有一个CPU, 哈哈）, 这时候谁能抢占CPU资源完全取决于waker和wakee的动态优先级(调度类优先级, 或者 CFS 的 vruntime 等, 依照进程的调度类而定), 如果wakee的动态优先级大于waker, 那么就标记waker的need_resched标志, 并在调度点到来的时候调用schedule函数进行调度。</p>
<p>SMP情况下, 由于系统的CPU资源比较多, waker和wakee没有必要争个你死我活, wakee其实也可以选择去其他CPU执行, 但是这时候要做决策:</p>
<ul>
<li>因为跑到 prev_cpu 上, 那么之前如果 cache 还是 hot 的是很有意义的</li>
<li>同时按照之前的假设 waker 和 wakee 之间有资源共享, 那么唤醒到 waker CPU 上也有好处</li>
<li>如果 prev_cpu, waker cpu 都很忙, 那放上来可以并不一定好, 唤醒延迟之类的都是一个考量.</li>
</ul>
<p>那么这些都是一个综合权衡的过程, 我们要考虑的东西比较多</p>
<ul>
<li>wake_cpu, prev_cpu 到底该不该选择？</li>
<li>选择的话选择哪个?</li>
<li>它们都不合适的时候又要怎么去选择一个更合适的?</li>
</ul>
<p>内核需要一个简单有效的机制去做这个事情, 因此 WAKE_AFFINE 出现在内核中.</p>
<h2 id="1-2-WAKE-AFFINE-机制简介"><a href="#1-2-WAKE-AFFINE-机制简介" class="headerlink" title="1.2    WAKE_AFFINE 机制简介"></a>1.2    WAKE_AFFINE 机制简介</h2><hr>
<p><a href=""><code>select_task_rq_fair</code></a> 选核其实是一个优选的过程, 通常会有限选择一个 cache-miss 等开销最小的一个</p>
<ol>
<li><p>根据 wake_affine 选择调度域并确定 new_cpu</p>
</li>
<li><p>根据调度域及其调度域参数选择兄弟 idle cpu 根据调度域及其调度域参数选择兄弟 idle cpu</p>
</li>
<li><p>根据调度域选择最深idle的cpu根据调度域选择最深idle的cpu find_idest_cpu</p>
</li>
</ol>
<p>在进程唤醒的过程中为进程选核时, wake_affine 倾向于将被唤醒进程尽可能安排在 waking CPU 上, 这样考虑的原因是: 有唤醒关系的进程是相互关联的, 尽可能地运行在具有 cache 共享的调度域中, 这样可以获得一些 chache-hit 带来的性能提升. 这时 wake_affine 的初衷, 但是这也是一把双刃剑.</p>
<p>将 wakee 都唤醒在 waker CPU 上, 必然造成 waker 和 wakee 的资源竞争. 特别是对于 1:N 的任务模型, wake_affine 会导致 waker 进程饥饿. </p>
<p>62470419e993f8d9d93db0effd3af4296ecb79a5    sched: Implement smarter wake-affine logic</p>
<p>因此后来 (COMMIT 62470419e993 “sched: Implement smarter wake-affine logic”), 实现了一种智能 wake-affine 的优化机制. 用于 wake_flips 的巧妙方式, 识别出 1:N 等复杂唤醒模型, 只有在认为 wake_affine 能提升性能时(want_affine)才进行 wake_affine.</p>
<h1 id="2-wake-affine-机制分析"><a href="#2-wake-affine-机制分析" class="headerlink" title="2    wake_affine 机制分析"></a>2    wake_affine 机制分析</h1><hr>
<p>根据 want_affine 变量选择调度域并确定 new_cpu</p>
<p>我们知道如下的事实 :</p>
<ul>
<li><p>进程p的调度域参数设置了SD_BALANCE_WAKE</p>
</li>
<li><p>当前cpu的唤醒次数没有超标</p>
</li>
<li><p>当前task p消耗的capacity <em> 1138小于min_cap </em> 1024</p>
</li>
<li><p>当前cpu在task p的cpu亲和数里面的一个</p>
</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// https://elixir.bootlin.com/linux/v5.1.15/source/kernel/sched/fair.c#L6674</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">int</span></span><br><span class="line">select_task_rq_fair(struct task_struct *p, <span class="keyword">int</span> prev_cpu, <span class="keyword">int</span> sd_flag, <span class="keyword">int</span> wake_flags)</span><br><span class="line">&#123;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sched_domain</span> *<span class="title">tmp</span>, *<span class="title">sd</span> = <span class="title">NULL</span>;</span></span><br><span class="line"> <span class="keyword">int</span> cpu = smp_processor_id();</span><br><span class="line"> <span class="keyword">int</span> new_cpu = prev_cpu;</span><br><span class="line"> <span class="keyword">int</span> want_affine = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> sync = (wake_flags &amp; WF_SYNC) &amp;&amp; !(current-&gt;flags &amp; PF_EXITING);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (sd_flag &amp; SD_BALANCE_WAKE) &#123;</span><br><span class="line">  record_wakee(p);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (sched_energy_enabled()) &#123;</span><br><span class="line">   new_cpu = find_energy_efficient_cpu(p, prev_cpu);</span><br><span class="line">   <span class="keyword">if</span> (new_cpu &gt;= <span class="number">0</span>)</span><br><span class="line">    <span class="keyword">return</span> new_cpu;</span><br><span class="line">   new_cpu = prev_cpu;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  want_affine = !wake_wide(p) &amp;&amp; !wake_cap(p, cpu, prev_cpu) &amp;&amp;</span><br><span class="line">         cpumask_test_cpu(cpu, &amp;p-&gt;cpus_allowed);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> rcu_read_lock();</span><br><span class="line"> for_each_domain(cpu, tmp) &#123;</span><br><span class="line">  <span class="keyword">if</span> (!(tmp-&gt;flags &amp; SD_LOAD_BALANCE))</span><br><span class="line">   <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/*</span></span><br><span class="line"><span class="comment">   * If both 'cpu' and 'prev_cpu' are part of this domain,</span></span><br><span class="line"><span class="comment">   * cpu is a valid SD_WAKE_AFFINE target.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">if</span> (want_affine &amp;&amp; (tmp-&gt;flags &amp; SD_WAKE_AFFINE) &amp;&amp;</span><br><span class="line">      cpumask_test_cpu(prev_cpu, sched_domain_span(tmp))) &#123;</span><br><span class="line">   <span class="keyword">if</span> (cpu != prev_cpu)</span><br><span class="line">    new_cpu = wake_affine(tmp, p, cpu, prev_cpu, sync);</span><br><span class="line"></span><br><span class="line">   sd = <span class="literal">NULL</span>; <span class="comment">/* Prefer wake_affine over balance flags */</span></span><br><span class="line">   <span class="keyword">break</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (tmp-&gt;flags &amp; sd_flag)</span><br><span class="line">   sd = tmp;</span><br><span class="line">  <span class="keyword">else</span> <span class="keyword">if</span> (!want_affine)</span><br><span class="line">   <span class="keyword">break</span>;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>wake_wide 和 wake_cap 为调度器提供决策, 当前进程是否符合 wake_affine 的决策模型. 如果他们返回 1, 则说明如果采用 wake_affine 进行决策, 大概率是无效的或者会降低性能, 则调度器就不会 want_affine 了.</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">want_affine = !wake_wide(p) &amp;&amp; !wake_cap(p, cpu, prev_cpu) &amp;&amp;</span><br><span class="line">         cpumask_test_cpu(cpu, &amp;p-&gt;cpus_allowed);</span><br></pre></td></tr></table></figure>
<p>wake_wide 检查当前cpu的唤醒关系符合 wake_affine 模型.<br>wake_cap 检查当前 task p 消耗的 CPU capacity 没有超出当前 CPU 的限制.<br>task p 可以在当前 CPU 上运行.</p>
<ul>
<li>wake_affine 则为目标进程选择最合适运行的 wake CPU.</li>
</ul>
<h2 id="2-1-want-affine"><a href="#2-1-want-affine" class="headerlink" title="2.1    want_affine"></a>2.1    want_affine</h2><hr>
<p>有 wakeup 关系的进程都是相互关联的进程, 那么大概率 waker 和 wakee 之间有一些数据共享, 这些数据可能是 waker 进程刚刚准备好的, 还在 cache 里面, 那么把它唤醒到 waking CPU, 就能充分利用这些在 cache 中的数据. 但是另外一方面, waker 之前在  prev CPU 上运行, 那么也是 cache-hot 的, 把它迁移到 waking CPU 上, 那么 prev CPU 上那些 cache 就有可能失效, 因此如果 waker 和 wakee 之间没有数据共享或者共享的数据没那么多, 那么wake_affine 直接迁移到 waking CPU 上反而是不合适的.</p>
<p>内核引入 wake_affine 的初衷就是识别什么时候要将 wakee 唤醒到 waking CPU, 什么时候不需要. 这个判断由 want_affine 通过 wake_cap() 和 wake_wide() 来完成.</p>
<h3 id="2-2-1-record-wakee-与-wakee-flips"><a href="#2-2-1-record-wakee-与-wakee-flips" class="headerlink" title="2.2.1    record_wakee 与 wakee_flips"></a>2.2.1    record_wakee 与 wakee_flips</h3><hr>
<blockquote>
<p>通过在 struct task_struct 中增加两个成员: 上次唤醒的进程 last_wakee, 和累积唤醒翻转计数器. 每当 waker 尝试唤醒 wakee 的时候, 就通过 record_wakee 来更新统计计数.</p>
</blockquote>
<p>在 select_task_rq_fair 开始的时候, 如果发现是 SD_BALANCE_WAKE,  则先会 record_wakee 统计 current 的 wakee_flips.</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">int</span></span><br><span class="line">select_task_rq_fair(struct task_struct *p, <span class="keyword">int</span> prev_cpu, <span class="keyword">int</span> sd_flag, <span class="keyword">int</span> wake_flags)</span><br><span class="line">&#123;</span><br><span class="line">        <span class="keyword">if</span> (sd_flag &amp; SD_BALANCE_WAKE) &#123;</span><br><span class="line">                record_wakee(p);</span><br></pre></td></tr></table></figure>
<p>wakee_flips 表示了当前进程作为 waker 时翻转(切换)其唤醒目标的次数, 所以高 wakee_flips 值意味着任务不止一个唤醒, 数字越大, 说明当前进程又不止一个 wakee, 而且唤醒频率越比较高. 且当前进程的 wakerr 数目 &lt; wakee_flips.</p>
<p>比如一个进程 P 连续一段时间的唤醒序列为: A, A, A, A, 那么由于没有翻转, 那么他的 wakee_flips 就始终为 1.</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">record_wakee</span><span class="params">(struct task_struct *p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * Only decay a single time; tasks that have less then 1 wakeup per</span></span><br><span class="line"><span class="comment">         * jiffy will not have built up many flips.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">if</span> (time_after(jiffies, current-&gt;wakee_flip_decay_ts + HZ)) &#123;</span><br><span class="line">                current-&gt;wakee_flips &gt;&gt;= <span class="number">1</span>;</span><br><span class="line">                current-&gt;wakee_flip_decay_ts = jiffies;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (current-&gt;last_wakee != p) &#123;</span><br><span class="line">                current-&gt;last_wakee = p;</span><br><span class="line">                current-&gt;wakee_flips++;</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>wakee_flips 有一定的衰减期, 如果过了 1S (即 1 个 HZ 的时间), 那么 wakee_flips 就衰减为原来的 1/2, 这就类似于 PELT 的指数衰减, Ns 前的 wakee_flips 的占比大概是当前这一个窗口的 1 / 2^N;</p>
<blockquote>
<p>全局变量jiffies用来记录自系统启动以来产生的节拍的总数(经过了多少tick). 启动时, 内核将该变量初始化为0, 此后, 每次时钟中断处理程序都会增加该变量的值.一秒内时钟中断的次数等于Hz, 所以jiffies一秒内增加的值也就是Hz.系统运行时间以秒为单位, 等于jiffies/Hz.<br>将以秒为单位的时间转化为jiffies：<br>seconds * Hz<br>将jiffies转化为以秒为单位的时间：<br>jiffies / Hz</p>
<p>jiffies记录了系统启动以来, .</p>
<p>一个tick代表多长时间, 在内核的CONFIG_HZ中定义.比如CONFIG_HZ=200, 则一个jiffies对应5ms时间.所以内核基于jiffies的定时器精度也是5ms</p>
</blockquote>
<h3 id="2-2-2-wake-wide"><a href="#2-2-2-wake-wide" class="headerlink" title="2.2.2    wake_wide"></a>2.2.2    wake_wide</h3><hr>
<p>当前 current 正在为 wakeup p, 并为 p 选择一个合适的 CPU. 那么 wake_wide 就用来检查 current 和 p 之间是否适合 wake_affine 所关心的 waker/wakee 模型.</p>
<p>wake_wide 返回 0, 表示 wake_affine 是有效的. 否则返回 1, 表示这两个进程不适合用 wake_affine.</p>
<p>那么什么时候, wake_wide 返回 1 ?</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Detect M:N waker/wakee relationships via a switching-frequency heuristic.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * A waker of many should wake a different task than the one last awakened</span></span><br><span class="line"><span class="comment"> * at a frequency roughly N times higher than one of its wakees.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * In order to determine whether we should let the load spread vs consolidating</span></span><br><span class="line"><span class="comment"> * to shared cache, we look for a minimum 'flip' frequency of llc_size in one</span></span><br><span class="line"><span class="comment"> * partner, and a factor of lls_size higher frequency in the other.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * With both conditions met, we can be relatively sure that the relationship is</span></span><br><span class="line"><span class="comment"> * non-monogamous, with partner count exceeding socket size.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Waker/wakee being client/server, worker/dispatcher, interrupt source or</span></span><br><span class="line"><span class="comment"> * whatever is irrelevant, spread criteria is apparent partner count exceeds</span></span><br><span class="line"><span class="comment"> * socket size.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">wake_wide</span><span class="params">(struct task_struct *p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">        <span class="keyword">unsigned</span> <span class="keyword">int</span> master = current-&gt;wakee_flips;</span><br><span class="line">        <span class="keyword">unsigned</span> <span class="keyword">int</span> slave = p-&gt;wakee_flips;</span><br><span class="line">        <span class="keyword">int</span> factor = this_cpu_read(sd_llc_size);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (master &lt; slave)</span><br><span class="line">                swap(master, slave);</span><br><span class="line">        <span class="keyword">if</span> (slave &lt; factor || master &lt; slave * factor)</span><br><span class="line">                <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>wake_affine 在决策的时候,  要参考 wakee_flips</p>
<ol>
<li>将 wakee_flips 值大的 wakee 唤醒到临近的 CPU,  可能有利于系统其他进程的唤醒,  同样这也意味着,  waker 将面临残酷的竞争.</li>
<li>此外,  如果 waker 也有一个很高的 wakee_flips,  那意味着多个任务依赖它去唤醒,  然后 1 中造成的 waker 的更高延迟会对这些唤醒造成负面影响,  因此一个高 wakee_flips 的 waker 再去将另外一个高 wakee_flips 的 wakee 唤醒到本地的 CPU 上,  是非常不明智的决策. 因此,  当 waker-&gt; wakee_flips / wakee-&gt; wakee_flips 变得越来越高时,  进行 wake_affine 操作的成本会很高.</li>
</ol>
<p>理解了这层含义, 那我们 wake_wide 的算法就明晰了. 如下情况我们认为决策是有效的 wake_affine </p>
<blockquote>
<p>factor = this_cpu_read(sd_llc_size); 这个因子表示了在当前 NODE 上能够共享 cache 的 CPU 数目(或者说当前sched_domain 中 CPU 的数目),   一个 sched_domain 中,   共享 chache 的 CPU 越多(比如 X86 上一个物理 CPU 上包含多个逻辑 CPU),   factor 就越大. 那么在 wake_affine 中的影响就是 wake_wide 返回 0 的概率更大,   那么 wake_affine 的结果有效的概率就更大. 因为有跟多的临近 CPU 可以选择,   这些 CPU 之间 cache 共享有优势.</p>
</blockquote>
<table>
<thead>
<tr>
<th style="text-align:center">条件</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">slave  &lt; factor</td>
<td style="text-align:center">即如果 wakee-&gt;wakee_flips &lt; factor,   则说明当前进程的唤醒切换不那么频繁,   即使当前进程有 wakee_flips 个 wakee,   当前 sched_domain 也完全能装的下他们.</td>
</tr>
<tr>
<td style="text-align:center">master &lt; slave * factor</td>
<td style="text-align:center">即 master/slave &lt; factor,   两个 waker wakee_flips 的比值小于 factor, 那么这种情况下, 进行 wake_affine 的成本可控.</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th style="text-align:center">commit</th>
<th style="text-align:center">patchwork</th>
<th style="text-align:center">lkml</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=63b0e9edceec10fa41ec33393a1515a5ff444277" target="_blank" rel="noopener">63b0e9edceec sched/fair: Beef up wake_wide</a></td>
<td style="text-align:center"><a href="https://lore.kernel.org/patchwork/patch/576823" target="_blank" rel="noopener">https://lore.kernel.org/patchwork/patch/576823</a></td>
<td style="text-align:center"><a href="https://lkml.org/lkml/2015/7/8/40" target="_blank" rel="noopener">https://lkml.org/lkml/2015/7/8/40</a></td>
</tr>
</tbody>
</table>
<h3 id="2-2-3-wake-cap"><a href="#2-2-3-wake-cap" class="headerlink" title="2.2.3    wake_cap"></a>2.2.3    wake_cap</h3><hr>
<p>由于目前有一些 CPU 都是属于性能异构的 CPU(比如 ARM64 的 big.LITTLE 等), 不同的核 capacity 会差很多. wake_cap 会先看待选择的进程是否</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">https:<span class="comment">//elixir.bootlin.com/linux/v5.6.13/source/kernel/sched/fair.c#L6128</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Disable WAKE_AFFINE in the case where task @p doesn't fit in the</span></span><br><span class="line"><span class="comment"> * capacity of either the waking CPU @cpu or the previous CPU @prev_cpu.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * In that case WAKE_AFFINE doesn't make sense and we'll let</span></span><br><span class="line"><span class="comment"> * BALANCE_WAKE sort things out.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">wake_cap</span><span class="params">(struct task_struct *p, <span class="keyword">int</span> cpu, <span class="keyword">int</span> prev_cpu)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"> <span class="keyword">long</span> min_cap, max_cap;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!static_branch_unlikely(&amp;sched_asym_cpucapacity))</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> min_cap = min(capacity_orig_of(prev_cpu), capacity_orig_of(cpu));</span><br><span class="line"> max_cap = cpu_rq(cpu)-&gt;rd-&gt;max_cpu_capacity;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Minimum capacity is close to max, no need to abort wake_affine */</span></span><br><span class="line"> <span class="keyword">if</span> (max_cap - min_cap &lt; max_cap &gt;&gt; <span class="number">3</span>)</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Bring task utilization in sync with prev_cpu */</span></span><br><span class="line"> sync_entity_load_avg(&amp;p-&gt;se);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> !task_fits_capacity(p, min_cap);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>注意在 <a href="https://lkml.org/lkml/2020/2/6/680" target="_blank" rel="noopener">sched/fair: Capacity aware wakeup rework</a> 合入之后, 通过 select_idle_sibling-=&gt;elect_idle_capacity 让 wakeup 感知了 capacity, 因此 原生的 wakeup 路径无需再做 capacity 相关的处理, 因此 wake_cap 就被干掉了. 参见<a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/kernel/sched/fair.c?id=000619680c3714020ce9db17eef6a4a7ce2dc28b" target="_blank" rel="noopener">sched/fair: Remove wake_cap()</a></p>
<h2 id="2-3-wake-affine"><a href="#2-3-wake-affine" class="headerlink" title="2.3    wake_affine"></a>2.3    wake_affine</h2><hr>
<p>如果 want_affine 发现对当前 wakee 进行 wake_affine 是有意义的, 那么就会为当前进程选择一个能尽快运行的 CPU. 它总是倾向于选择 waking CPU(this_cpu) 以及 prev_cpu.</p>
<p>其中</p>
<ul>
<li><p>wake_affine_idle 则看 prev_cpu 以及 this_cpu 是不是处于 cache 亲和的以及是不是idle 状态, 这样的 CPU<br>往往是最佳的.</p>
</li>
<li><p>wake_affine_weight 则进一步考虑进程的负载信息以及调度的延迟信息.</p>
</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * The purpose of wake_affine() is to quickly determine on which CPU we can run</span></span><br><span class="line"><span class="comment"> * soonest. For the purpose of speed we only consider the waking and previous</span></span><br><span class="line"><span class="comment"> * CPU.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * wake_affine_idle() - only considers 'now', it check if the waking CPU is</span></span><br><span class="line"><span class="comment"> * cache-affine and is (or will be) idle.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * wake_affine_weight() - considers the weight to reflect the average</span></span><br><span class="line"><span class="comment"> * scheduling latency of the CPUs. This seems to work</span></span><br><span class="line"><span class="comment"> * for the overloaded case.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">wake_affine</span><span class="params">(struct sched_domain *sd, struct task_struct *p,</span></span></span><br><span class="line"><span class="function"><span class="params">                       <span class="keyword">int</span> this_cpu, <span class="keyword">int</span> prev_cpu, <span class="keyword">int</span> sync)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">        <span class="keyword">int</span> target = nr_cpumask_bits;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (sched_feat(WA_IDLE))</span><br><span class="line">                target = wake_affine_idle(this_cpu, prev_cpu, sync);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (sched_feat(WA_WEIGHT) &amp;&amp; target == nr_cpumask_bits)</span><br><span class="line">                target = wake_affine_weight(sd, p, this_cpu, prev_cpu, sync);</span><br><span class="line"></span><br><span class="line">        schedstat_inc(p-&gt;se.statistics.nr_wakeups_affine_attempts);</span><br><span class="line">        <span class="keyword">if</span> (target == nr_cpumask_bits)</span><br><span class="line">                <span class="keyword">return</span> prev_cpu;</span><br><span class="line"></span><br><span class="line">        schedstat_inc(sd-&gt;ttwu_move_affine);</span><br><span class="line">        schedstat_inc(p-&gt;se.statistics.nr_wakeups_affine);</span><br><span class="line">        <span class="keyword">return</span> target;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="2-3-1-负载计算方式"><a href="#2-3-1-负载计算方式" class="headerlink" title="2.3.1    负载计算方式"></a>2.3.1    负载计算方式</h3><hr>
<p>wake_affine 函数源码分析之前, 需要先知道三个load的计算方式如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">source_load(<span class="keyword">int</span> cpu, <span class="keyword">int</span> type)</span><br><span class="line">target_load(<span class="keyword">int</span> cpu, <span class="keyword">int</span> type)target_load(<span class="keyword">int</span> cpu, <span class="keyword">int</span> type)</span><br><span class="line">effective_load(struct task_group *tg, <span class="keyword">int</span> cpu, <span class="keyword">long</span> wl, <span class="keyword">long</span> wg)</span><br></pre></td></tr></table></figure>
<p>根据调度类和 “nice” 值, 对迁移源 CPU 和目的 CPU 的负载 source_load 和 target_load 进行估计.<br>对于 source_load 我们采用保守的方式进行估计, 对于 target_load 则倾向于偏激. 因此当 type 传入的值非 0 时, source_load 返回最小值, 而 target_load 返回最大值. 当 type == 0 时, 将直接返回 weighted_cpuload</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#https:<span class="comment">//elixir.bootlin.com/linux/v4.14.14/source/kernel/sched/fair.c#5258</span></span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Return a low guess at the load of a migration-source CPU weighted</span></span><br><span class="line"><span class="comment"> * according to the scheduling class and "nice" value.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * We want to under-estimate the load of migration sources, to</span></span><br><span class="line"><span class="comment"> * balance conservatively.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="title">source_load</span><span class="params">(<span class="keyword">int</span> cpu, <span class="keyword">int</span> type)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">rq</span> *<span class="title">rq</span> = <span class="title">cpu_rq</span>(<span class="title">cpu</span>);</span></span><br><span class="line">        <span class="keyword">unsigned</span> <span class="keyword">long</span> total = weighted_cpuload(rq);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (type == <span class="number">0</span> || !sched_feat(LB_BIAS))</span><br><span class="line">                <span class="keyword">return</span> total;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> min(rq-&gt;cpu_load[type<span class="number">-1</span>], total);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#https:<span class="comment">//elixir.bootlin.com/linux/v4.14.14/source/kernel/sched/fair.c#5280</span></span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Return a high guess at the load of a migration-target CPU weighted</span></span><br><span class="line"><span class="comment"> * according to the scheduling class and "nice" value.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="title">target_load</span><span class="params">(<span class="keyword">int</span> cpu, <span class="keyword">int</span> type)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">rq</span> *<span class="title">rq</span> = <span class="title">cpu_rq</span>(<span class="title">cpu</span>);</span></span><br><span class="line">        <span class="keyword">unsigned</span> <span class="keyword">long</span> total = weighted_cpuload(rq);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (type == <span class="number">0</span> || !sched_feat(LB_BIAS))</span><br><span class="line">                <span class="keyword">return</span> total;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> max(rq-&gt;cpu_load[type<span class="number">-1</span>], total);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#https:<span class="comment">//elixir.bootlin.com/linux/v4.14.14/source/kernel/sched/fair.c#5139</span></span></span><br><span class="line"><span class="comment">/* Used instead of source_load when we know the type == 0 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="title">weighted_cpuload</span><span class="params">(struct rq *rq)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> cfs_rq_runnable_load_avg(&amp;rq-&gt;cfs);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="2-3-2-wake-affine-idle"><a href="#2-3-2-wake-affine-idle" class="headerlink" title="2.3.2    wake_affine_idle"></a>2.3.2    wake_affine_idle</h3><hr>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">int</span></span><br><span class="line">wake_affine_idle(<span class="keyword">int</span> this_cpu, <span class="keyword">int</span> prev_cpu, <span class="keyword">int</span> sync)</span><br><span class="line">&#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * If this_cpu is idle, it implies the wakeup is from interrupt</span></span><br><span class="line"><span class="comment">         * context. Only allow the move if cache is shared. Otherwise an</span></span><br><span class="line"><span class="comment">         * interrupt intensive workload could force all tasks onto one</span></span><br><span class="line"><span class="comment">         * node depending on the IO topology or IRQ affinity settings.</span></span><br><span class="line"><span class="comment">         *</span></span><br><span class="line"><span class="comment">         * If the prev_cpu is idle and cache affine then avoid a migration.</span></span><br><span class="line"><span class="comment">         * There is no guarantee that the cache hot data from an interrupt</span></span><br><span class="line"><span class="comment">         * is more important than cache hot data on the prev_cpu and from</span></span><br><span class="line"><span class="comment">         * a cpufreq perspective, it's better to have higher utilisation</span></span><br><span class="line"><span class="comment">         * on one CPU.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">if</span> (available_idle_cpu(this_cpu) &amp;&amp; cpus_share_cache(this_cpu, prev_cpu))</span><br><span class="line">                <span class="keyword">return</span> available_idle_cpu(prev_cpu) ? prev_cpu : this_cpu;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (sync &amp;&amp; cpu_rq(this_cpu)-&gt;nr_running == <span class="number">1</span>)</span><br><span class="line">                <span class="keyword">return</span> this_cpu;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> nr_cpumask_bits;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果 this_cpu 空闲, 则意味着唤醒来自中断上下文. 仅在 this_cpu 和 prev_cpu 有共享缓存时允许移动. 否则, 中断密集型工作负载可能会将所有任务强制到一个节点, 具体取决于IO拓扑或IRQ亲缘关系设置. 同时如果 prev_cpu 也是空闲的, 优先 prev_cpu.</p>
<p>另外没有证据保证来自中断的缓存热数据比 prev_cpu 上的缓存热数据更重要, 并且从cpufreq的角度来看, 最好在一个CPU上获得更高的利用率.</p>
<h3 id="2-3-3-wake-affine-weight"><a href="#2-3-3-wake-affine-weight" class="headerlink" title="2.3.3    wake_affine_weight"></a>2.3.3    wake_affine_weight</h3><hr>
<p><code>wake_affine_weight</code> 会重新计算 <code>wakeup CPU</code> 和 <code>prev CPU</code> 的负载情况, 如果 <code>wakeup CPU</code> 的负载加上唤醒进程的负载比 <code>prev CPU</code> 的负载小, 那么 <code>wakeup CPU</code> 是可以唤醒进程.</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">int</span></span><br><span class="line">wake_affine_weight(struct sched_domain *sd, struct task_struct *p,</span><br><span class="line">                   <span class="keyword">int</span> this_cpu, <span class="keyword">int</span> prev_cpu, <span class="keyword">int</span> sync)</span><br><span class="line">&#123;</span><br><span class="line">        s64 this_eff_load, prev_eff_load;</span><br><span class="line">        <span class="keyword">unsigned</span> <span class="keyword">long</span> task_load;</span><br><span class="line"></span><br><span class="line">        this_eff_load = target_load(this_cpu, sd-&gt;wake_idx);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (sync) &#123;</span><br><span class="line">                <span class="keyword">unsigned</span> <span class="keyword">long</span> current_load = task_h_load(current);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (current_load &gt; this_eff_load)</span><br><span class="line">                        <span class="keyword">return</span> this_cpu;</span><br><span class="line"></span><br><span class="line">                this_eff_load -= current_load;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        task_load = task_h_load(p);</span><br><span class="line"></span><br><span class="line">        this_eff_load += task_load;</span><br><span class="line">        <span class="keyword">if</span> (sched_feat(WA_BIAS))</span><br><span class="line">                this_eff_load *= <span class="number">100</span>;</span><br><span class="line">        this_eff_load *= capacity_of(prev_cpu);</span><br><span class="line"></span><br><span class="line">        prev_eff_load = source_load(prev_cpu, sd-&gt;wake_idx);</span><br><span class="line">        prev_eff_load -= task_load;</span><br><span class="line">        <span class="keyword">if</span> (sched_feat(WA_BIAS))</span><br><span class="line">                prev_eff_load *= <span class="number">100</span> + (sd-&gt;imbalance_pct - <span class="number">100</span>) / <span class="number">2</span>;</span><br><span class="line">        prev_eff_load *= capacity_of(this_cpu);</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * If sync, adjust the weight of prev_eff_load such that if</span></span><br><span class="line"><span class="comment">         * prev_eff == this_eff that select_idle_sibling() will consider</span></span><br><span class="line"><span class="comment">         * stacking the wakee on top of the waker if no other CPU is</span></span><br><span class="line"><span class="comment">         * idle.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">if</span> (sync)</span><br><span class="line">                prev_eff_load += <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> this_eff_load &lt; prev_eff_load ? this_cpu : nr_cpumask_bits;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我们假设将进程从 prev CPU 迁移到了 wakeup CPU, 那么 this_eff_load  记录了迁移后 wakeup CPU 的负载, 那么 prev_eff_load 则是迁移后 prev CPU 的负载.</p>
<p>eff_load 的计算方式为:</p>
<p>$$<br>x={[cpu_load\pm\task_h_load(p)]\times{capacity_of(cpu)}}<br>$$</p>
<p>注意使用 wake_affine_weight 需要开启 WA_WEIGHT 开关</p>
<table>
<thead>
<tr>
<th style="text-align:center">描述</th>
<th style="text-align:center">commit</th>
<th style="text-align:center">分析</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">smart wake-affine(<a href="https://lkml.org/lkml/2017/10/14/172" target="_blank" rel="noopener">lkml</a>,patchwork)</td>
</tr>
</tbody>
</table>
<p>wake_affine_weight 中负载比较的部分经历了很多次的修改.<br><a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=eeb60398639143c11ff2c8b509e3a471411bb5d3" target="_blank" rel="noopener">eeb603986391 sched/fair: Defer calculation of ‘prev_eff_load’ in wake_affine_weight() until needed</a><br><a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=082f764a2f3f2968afa1a0b04a1ccb1b70633844" target="_blank" rel="noopener">082f764a2f3f sched/fair: Do not migrate on wake_affine_weight() if weights are equal</a><br><a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1c1b8a7b03ef50f80f5d0c871ee261c04a6c967e" target="_blank" rel="noopener">1c1b8a7b03ef sched/fair: Replace source_load() &amp; target_load() with weighted_cpuload()</a>, 这个是 <a href="https://lore.kernel.org/patchwork/patch/456549/" target="_blank" rel="noopener">sched: remove cpu_loads</a> 中的一个补丁, 该补丁集删除了 cpu_load idx 干掉了 LB_BIAS 特性, 它指出 LB_BIAS 的设计本身是有问题的, 在负载均衡迁移时平滑两个 cpu_load 的过程中, 用 source_load/target_load 的方式在源 CPU 和目的 CPU 上用一个随机偏差的方式是错误的, 这个平衡偏差应该取决于cpu组之间的任务转移成本, 而不是随机历史记录或即时负载。因为历史负载可能与实际负载相差很大, 从而导致不正确的偏差.<br><a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=11f10e5420f6cecac7d4823638bff040c257aba9" target="_blank" rel="noopener">11f10e5420f6c sched/fair: Use load instead of runnable load in wakeup path</a> <a href="https://lore.kernel.org/patchwork/patch/1141693" target="_blank" rel="noopener">https://lore.kernel.org/patchwork/patch/1141693</a>, 该补丁是 rework load balancce 的一个补丁, 之前唤醒路径用下的是 cpu_runnable_load, 现在修正为 cpu_load. cpu_load 对应的是 rq 的 load_avg, 代表就绪队列平均负载, 其包含睡眠进程的负载贡献, cpu_runnable_load 则是 runnable_load_avg只包含就绪队列上所有可运行进程的负载贡献,  wakeup 的时候如果使用 cpu_runnable_load 则可能造成选核的时候选择到一个有很多 runnable 线程的 overloaded 的 CPU, 而不是一个有很多 blocked 线程, 但是还有很大空闲的 CPU. 因此使用 cpu_load 在 wakeup 的时候可能更好.<br>当前内核版本 5.6.13 中 wake_affine_weight 的实现<a href="https://elixir.bootlin.com/linux/v5.6.13/source/kernel/sched/fair.c#L5556" target="_blank" rel="noopener">参见</a>, 跟我们前面将的思路没有太大变化, 但是没有了 LB_BIAS, 同时比较负载使用的是 cpu_load().</p>
<h2 id="2-4-wake-affine-演进"><a href="#2-4-wake-affine-演进" class="headerlink" title="2.4    wake_affine 演进"></a>2.4    wake_affine 演进</h2><hr>
<p>Michael Wang 实现了 Smart wake affine, 引入 wakee_flips 来识别 wake-affine 的场景. 然后 Peter 做了一个简单的优化, factor 使用了 sd-&gt;sd_llc_size 而不是直接获取所在NODE 的 CPU 数目. nr_cpus_node(cpu_to_node(smp_processor_id()));</p>
<table>
<thead>
<tr>
<th style="text-align:center">commit</th>
<th style="text-align:center">lkml</th>
<th style="text-align:center">patchwork</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=62470419e993f8d9d93db0effd3af4296ecb79a5" target="_blank" rel="noopener">62470419e993 sched: Implement smarter wake-affine logic</a><br><a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7d9ffa8961482232d964173cccba6e14d2d543b2" target="_blank" rel="noopener">7d9ffa896148 sched: Micro-optimize the smart wake-affine logic</a></td>
<td style="text-align:center"><a href="https://lkml.org/lkml/2013/7/4/18" target="_blank" rel="noopener">https://lkml.org/lkml/2013/7/4/18</a></td>
<td style="text-align:center"><a href="https://lore.kernel.org/patchwork/cover/390846" target="_blank" rel="noopener">https://lore.kernel.org/patchwork/cover/390846</a></td>
</tr>
</tbody>
</table>
<p>接着 Vincent Guittot 和 Rik van Riel 做了不少功耗优化的工作. 这时候 wake-affne 中开始考虑 CPU capacity 的信息.</p>
<table>
<thead>
<tr>
<th style="text-align:center">commit</th>
<th style="text-align:center">lkml</th>
<th style="text-align:center">patchwork</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><a href="">05bfb65f52cb sched: Remove a wake_affine() condition</a><br><a href="">bd61c98f9b3f sched: Test the CPU’s capacity in wake_affine()</a></td>
<td style="text-align:center"><a href="https://lkml.org/lkml/2014/5/23/458" target="_blank" rel="noopener">https://lkml.org/lkml/2014/5/23/458</a></td>
<td style="text-align:center">XXXX</td>
</tr>
</tbody>
</table>
<p>然后 Rik van Riel 在 NUMA 层次支持了 wake_affine </p>
<table>
<thead>
<tr>
<th style="text-align:center">commit</th>
<th style="text-align:center">lkml</th>
<th style="text-align:center">patchwork</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">739294fb03f5 sched/numa: Override part of migrate_degrades_locality() when idle balancing<br>7d894e6e34a5 sched/fair: Simplify wake_affine() for the single socket case<br>3fed382b46ba sched/numa: Implement NUMA node level wake_affine()<br>815abf5af45f sched/fair: Remove effective_load()<br></td>
<td style="text-align:center"><a href="https://lkml.org/lkml/2017/6/23/496" target="_blank" rel="noopener">https://lkml.org/lkml/2017/6/23/496</a></td>
<td style="text-align:center"><a href="https://lore.kernel.org/patchwork/cover/803114/" target="_blank" rel="noopener">https://lore.kernel.org/patchwork/cover/803114/</a></td>
</tr>
</tbody>
</table>
<p>紧接着是 Peter Zijlstra 的一堆 FIX, 为了解决支持了 NUMA 之后一系列性能问题.</p>
<table>
<thead>
<tr>
<th style="text-align:center">commit</th>
<th style="text-align:center">lkml</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">90001d67be2f sched/fair: Fix wake_affine() for !NUMA_BALANCING</td>
<td style="text-align:center"><a href="https://lkml.org/lkml/2017/8/1/377" target="_blank" rel="noopener">https://lkml.org/lkml/2017/8/1/377</a></td>
<td style="text-align:center">XXXX</td>
</tr>
<tr>
<td style="text-align:center">a731ebe6f17b sched/fair: Fix wake_affine_llc() balancing rules</td>
<td style="text-align:center"><a href="https://lkml.org/lkml/2017/9/6/196" target="_blank" rel="noopener">https://lkml.org/lkml/2017/9/6/196</a></td>
<td style="text-align:center">XXXX</td>
</tr>
<tr>
<td style="text-align:center">d153b153446f sched/core: Fix wake_affine() performance regression<br>f2cdd9cc6c97 sched/core: Address more wake_affine() regressions</td>
<td style="text-align:center"><a href="https://lkml.org/lkml/2017/10/14/172" target="_blank" rel="noopener">https://lkml.org/lkml/2017/10/14/172</a></td>
<td style="text-align:center">该补丁引入了 WA_IDLE, WA_BIAS+WA_WEIGHT</td>
</tr>
</tbody>
</table>
<p>目前最新 5.2 的内核中,<br>Dietmar Eggemann 删除了 LB_BIAS 特性, 因此 wake-affine 的代码做了部分精简.(仅仅是代码重构, 没有逻辑变更)</p>
<table>
<thead>
<tr>
<th style="text-align:center">commit</th>
<th style="text-align:center">lkml</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">fdf5f315d5cf sched/fair: Disable LB_BIAS by default</td>
<td style="text-align:center"><a href="https://lkml.org/lkml/2018/8/9/493" target="_blank" rel="noopener">https://lkml.org/lkml/2018/8/9/493</a></td>
<td style="text-align:center">默认 LB_BIAS 为 false</td>
</tr>
<tr>
<td style="text-align:center">1c1b8a7b03ef sched/fair: Replace source_load() &amp; target_load() with weighted_cpuload()</td>
<td style="text-align:center">没有 LB_BIAS 之后, source_load/target_load 不再需要, 直接使用 weighted_cpuload 代替</td>
</tr>
<tr>
<td style="text-align:center">a3df067974c5 sched/fair: Rename weighted_cpuload() to cpu_runnable_load()</td>
<td style="text-align:center">weighted_cpuload 函数更名为 cpu_runnable_load, <a href="https://lore.kernel.org/patchwork/cover/1079333/" target="_blank" rel="noopener">patchwork</a></td>
</tr>
</tbody>
</table>
<h1 id="3-wake-affine-对-select-task-rq-fair-的影响"><a href="#3-wake-affine-对-select-task-rq-fair-的影响" class="headerlink" title="3    wake_affine 对 select_task_rq_fair 的影响."></a>3    wake_affine 对 select_task_rq_fair 的影响.</h1><hr>
<p>在唤醒CFS 进程的时候通过 select_task_rq_fair 来为进程选择一个最适合的 CPU.<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">try_to_wake_up</span><br><span class="line">cpu = select_task_rq(p, p-&gt;wake_cpu, SD_BALANCE_WAKE, wake_flags);</span><br></pre></td></tr></table></figure></p>
<p>那么在 wake_affine 机制的参与下, 选核流程是一个什么样的逻辑呢?<br><a href="https://elixir.bootlin.com/linux/v5.1.15/source/kernel/sched/fair.c#L6661" target="_blank" rel="noopener">代码参见</a>, 选用早期  v5.1.15 版本的内核.</p>
<ul>
<li>首先 sd_flag 必须配置 SD_BALANCE_WAKE 才会去做 wake_affine, 如果是 energy aware, EAS 会先通过 find_energy_efficient_cpu 选核, 不过这个是 EAS 的范畴, 不是我们今天的重点.</li>
<li>先 record_wakee 更新 wake affine 统计信息, 接着通过 wake_cap 和 wake_wide 看这次对进程的唤醒是不是 want_affine 的.</li>
<li>接着从 waker CPU 开始向上遍历调度域, <ol>
<li>如果是 want_affine, 则先通过 wake_affine 在当前调度域 tmp 中是从 prev_cpu 和 waker CPU 以及上次的 waker CPU( recent_used_cpu) 中优选一个合适的 new CPU, 待会选核的时候, 就会从走快速路径 select_idle_sibling 中从 prev_cpu 和 new cpu 中优选一个 CPU. 同时设置 recent_used_cpu 为当前 waker CPU</li>
<li>否则, 如果是 want_affine, 但是 tmp 中没找到满足要求的 CPU,  则最终循环结束条件为 !(tmp-&gt;flag &amp; SD_LOAD_BALANCE), 同样如果不是 want_affine 的, 则最终循环结束条件为 !(tmp-&gt;flag &amp; SD_LOAD_BALANCE)  或者 !tmp-&gt;flags &amp; sd_flag,则 sd 指向了配置了 SD_LOAD_BALANCE 和 sd_flag 的最高那个层次的 sd, 这个时候会通过 find_idlest_cpu 慢速路径选择, 从这个 sd 中选择一个 idle 或者负载最小的 CPU. </li>
</ol>
</li>
</ul>
<p>只要 wakeup 的时候, 会通过 wake_affine, 然后通过 select_idle_sibling 来选核.<br>其他情况下, 都是找到满足 sd_flags 的最高层次 sd, 然后通过 find_idlest_cpu 在这个调度域 sd 中去选择一个最空闲的 CPU.</p>
<h1 id="4-参考资料"><a href="#4-参考资料" class="headerlink" title="4    参考资料"></a>4    参考资料</h1><hr>
<p><a href="https://lwn.net/Articles/741726/" target="_blank" rel="noopener"><code>Reduce scheduler migrations due to wake_affine</code></a></p>
<p><a href="http://www.pianshen.com/article/267721013//" target="_blank" rel="noopener">[scheduler]十. 传统的负载均衡是如何为task选择合适的cpu？</a></p>
<p><a href="https://blog.csdn.net/wuming_422103632/column/info/26430" target="_blank" rel="noopener">wukongmingjing 的调度器专栏</a></p>
<p><a href="https://stackoverflow.com/questions/34442691/linux-kernel-task-h-load" target="_blank" rel="noopener">Linux Kernel- task_h_load</a></p>

    </div>
    <p class="eof">-- EOF --</p>
    <p class="post-meta">
        <span class="post-cat">分类：
            <a class="cat-link" href="/categories/内核探秘/">内核探秘</a>
        </span>
        <span class="post-tags">
            标签：
            
    
        <a href="/tags/linux/" title="linux">linux</a> / 
    
        <a href="/tags/scheduler/" title="scheduler">scheduler</a> / 
    
        <a href="/tags/kernel/" title="kernel">kernel</a>
    

        </span>
    </p>
</article>
<!-- 分享按钮 -->

  <div class="article-share clearfix text-center">
    <div class="share-area">
      <span class="share-txt">分享到：</span>
      <a href="javascript: window.open('http://service.weibo.com/share/share.php?url=' + encodeURIComponent(location.href) + '&title=' + document.title + '&language=zh_cn');" class="share-icon weibo"></a>
      <a href="javascript: alert('请复制链接到微信并发送');" class="share-icon wechat"></a>
      <a href="javascript: window.open('http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=' + encodeURIComponent(location.href) + '&title=' + document.title);" class="share-icon qqzone"></a>
      <a href="javascript: window.open('http://connect.qq.com/widget/shareqq/index.html?url=' + encodeURIComponent(location.href) + '&desc=Gatieme个人博客&title=' + document.title + '&callback=' + encodeURIComponent(location.href));" class="share-icon qq"></a>
      <a href="javascript: window.open('http://shuo.douban.com/!service/share?href=' + encodeURIComponent(location.href) + '&name=' + document.title + '&text=' + document.title);" class="share-icon douban"></a>
    </div>
  </div>



<!-- 上一篇/下一篇 -->

<div class="article-nav clearfix">
    
    <span class="prev fl">
        上一篇<br>
        <a href="/2020/07/18/2020/0718-0001-Cmder_Chinese_garbled_settings/">
            
                Cmder 中文乱码设置
            
        </a>
    </span>
    

    
    <span class="next fr">
        下一篇<br>
        <a href="/2018/11/17/2018/1117-0001-use_glibc_backtrace_to_report_active_stack_frames_userspace/">
            
                用户态使用 glibc/backtrace 追踪函数调用堆栈定位段错误
            
        </a>
    </span>
    
</div>

<!-- 文章评论 -->

  <script src="/js/comment.js?v=1615735432630"></script>
  <div id="comments" class="comment">
    <!--
    <div class="sign-bar">
      GitHub 已登录!
      <span class="sign-link">登出</span>
    </div>
    <section class="box">
      <div class="com-avatar"><img src="/img/gatieme.jpg" alt="avatar"></div>
      <div class="com-text">
        <div class="main">
          <textarea class="text-area-edited show" placeholder="欢迎评论！"></textarea>
          <div class="text-area-preview"></div>
        </div>
        <div class="switch">
          <div class="switch-item on">编辑</div>
          <div class="switch-item">预览</div>
        </div>
        <div class="button">提交</div>
      </div>
    </section>
    <section class="tips">注：评论支持 markdown 语法！</section>
    <section class="list-wrap">
      <ul class="list">
        <li>
          <div class="user-avatar">
            <a href="/">
              <img src="/img/gatieme.jpg" alt="user-avatar">
            </a>
          </div>
          <div class="user-comment">
            <div class="user-comment-header">
              <span class="post-name">张德龙</span>
              <span class="post-time">2017年12月12日</span>
              <span class="like liked">已赞</span>
              <span class="like-num">2</span>
            </div>
            <div class="user-comment-body">333333</div>
          </div>
        </li>
        <li>
          <div class="user-avatar">
            <a href="/">
              <img src="/img/gatieme.jpg" alt="user-avatar">
            </a>
          </div>
          <div class="user-comment">
            <div class="user-comment-header">
              <span class="post-name">刘德华</span>
              <span class="post-time">2017年12月12日</span>
              <span class="like">点赞</span>
              <span class="like-num">2</span>
            </div>
            <div class="user-comment-body">vvvvv</div>
          </div>
        </li>
      </ul>
      <div class="page-nav">
        <a href="javascript: void(0);" class="item">1</a>
        <a href="javascript: void(0);" class="item">2</a>
        <a href="javascript: void(0);" class="item current">3</a>
      </div>
    </section>
    -->
  </div>
  <script>
  JELON.Comment({
    container: 'comments',
    label: '2020/0524-0001-linux_cfs_scheduler_31_wake_affine' || '2020/05/24/2020/0524-0001-linux_cfs_scheduler_31_wake_affine/',
    owner: 'gatieme',
    repo: 'blog_comments',
    clientId: 'cf796c25fcf4369b9295',
    clientSecret: 'c2ecfa50b9914a8fc9655ede7c9e6cf2ff84a70c'
  });
  </script>




            </div>
        </section>
        <!-- 侧栏部分 -->
<aside class="sidebar">
    
    <section class="widget">
        <h3 class="widget-hd"><strong>文章搜索</strong></h3>
        <div class="search-form">
  <form id="searchForm" method="GET" action="https://www.google.com/search" ectype="application/x-www-form-urlencoded" target="_blank" autocomplete="false" onsubmit="javascript: return false;">
    <input id="searchKeyword" type="text" class="form-control" placeholder="输入关键字搜索" autocomplete="false">
    <input id="searchKeywordHidden" type="hidden" name="q">
    <input id="searchButton" class="btn" type="submit" value="搜索">
  </form>
</div>

    </section>
    

    <section class="widget">
        <h3 class="widget-hd"><strong>文章分类</strong></h3>
        <!-- 文章分类 -->
<ul class="widget-bd">
    
    <li>
        <a href="/categories/linux/">linux</a>
        <span class="badge">(0)</span>
    </li>
    
    <li>
        <a href="/categories/hexo/">hexo</a>
        <span class="badge">(0)</span>
    </li>
    
    <li>
        <a href="/categories/qemu/">qemu</a>
        <span class="badge">(0)</span>
    </li>
    
    <li>
        <a href="/categories/debug/">debug</a>
        <span class="badge">(0)</span>
    </li>
    
    <li>
        <a href="/categories/编程珠玑/">编程珠玑</a>
        <span class="badge">(7)</span>
    </li>
    
    <li>
        <a href="/categories/技术积累/">技术积累</a>
        <span class="badge">(3)</span>
    </li>
    
    <li>
        <a href="/categories/内核探秘/">内核探秘</a>
        <span class="badge">(3)</span>
    </li>
    
</ul>
    </section>

    
    <section class="widget">
        <h3 class="widget-hd"><strong>热门标签</strong></h3>
        <!-- 文章标签 -->
<div class="widget-bd tag-wrap">
  
    <a class="tag-item" href="/tags/linux/" title="linux">linux (10)</a>
  
    <a class="tag-item" href="/tags/debug/" title="debug">debug (6)</a>
  
    <a class="tag-item" href="/tags/tools/" title="tools">tools (4)</a>
  
    <a class="tag-item" href="/tags/scheduler/" title="scheduler">scheduler (3)</a>
  
    <a class="tag-item" href="/tags/hexo/" title="hexo">hexo (2)</a>
  
    <a class="tag-item" href="/tags/blog/" title="blog">blog (2)</a>
  
    <a class="tag-item" href="/tags/qemu/" title="qemu">qemu (1)</a>
  
    <a class="tag-item" href="/tags/topdown/" title="topdown">topdown (1)</a>
  
    <a class="tag-item" href="/tags/kernel/" title="kernel">kernel (1)</a>
  
</div>
    </section>
    

    

    
    <!-- 友情链接 -->
    <section class="widget">
        <h3 class="widget-hd"><strong>友情链接</strong></h3>
        <!-- 文章分类 -->
<ul class="widget-bd">
    
        <li>
            <a href="https://blog.csdn.net/gatieme" target="_blank" title="我的 CSDN 博客">CSDN--紫夜阑珊-青伶巷草</a>
        </li>
    
        <li>
            <a href="https://xydh.fun/gatieme" target="_blank" title="我的炫猿导航主页">炫猿导航</a>
        </li>
    
        <li>
            <a href="https://jelon.info" target="_blank" title="Jelon 的博客">Jelon 的博客</a>
        </li>
    
</ul>
    </section>
    
</aside>
<!-- / 侧栏部分 -->
    </div>

    <!-- 博客底部 -->
    <footer class="footer">
    &copy;
    
        2014-2021
    

    <a href="/">紫夜阑珊-青伶巷草</a>
</footer>
<div class="back-to-top" id="JELON__backToTop" title="返回顶部">返回顶部</div>

    <!--博客js脚本 -->
    <!-- 这里放网站js脚本 -->
<script src="/js/main.js?v=1615735433163"></script>

</body>
</html>
